package com.runewbie.pafcmall.search.thread;

import java.util.concurrent.*;

/**
 * @author: li_jian
 * @version: 1.0
 * @date: 2021/3/2 22:20
 * @description：
 */
public class TreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void thread() throws ExecutionException, InterruptedException {
        /**
         * 初始化线程的4种方式
         *
         * 1)、继承Thread
         *  Thread01 thread01 = new Thread01();
         *  thread01.start();
         * 2)、实现Runnable接口
         *  Runnable01 runnable01 = new Runnable01();
         *  new Thread(runnable01).start();
         * 3)、实现Callable接口+ FutureTask (可以拿到返回结果,可以处理异常)
         *  FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
         *  new Thread(futureTask).start();
         *  // 等待整个线程执行完成，获取返回结果
         *  Integer integer = futureTask.get();
         * 4)、线程池
         *      线程池直接提交任务
         *      executorService.execute(new Runnable01())
         *      1、创建
         *          1）、Executors
         *          2）、 new ThreadPoolExecutor()
         *
         * 区别：
         *  1、2不能得到返回值，3可以获取返回值
         *  1、2、3都不能控制资源
         *  4可以控制资源，性能稳定
         */
        // 我们在业务代码里面，1、2、3启动线程的方式都不用，将所有的多线程异步任务都交给线程池执行。

        /**
         * 七大参数
         * int corePoolSize[5]核心线程数，一直存在，除非设置allowCoreThreadTimeOut；线程池创建好以后就准备就绪的线程数量，就等待来
         * 接收异步任务去执行
         * int maximumPoolSize,[200]最大线程数量；控制资源并发
         * long keepAliveTime,存活时间；如果当前线程数量大于core数量，
         * 释放空闲的线程（maximumPoolSize-corePoolSize）。
         * 只要线程空闲时间大于指定的keepAliveTime
         * TimeUnit unit,时间单位
         * BlockingQueue<Runnable> workQueue,阻塞队列；如果任务有很多，就会将目前多的任务放到队列里边。
         * 只要线程空闲，就会从队列里边取出新的任务继续执行
         * ThreadFactory threadFactory,线程的创建工厂
         * RejectedExecutionHandler handler 如果队列满了，按照指定的拒绝策略执行拒绝任务
         *
         * 工作顺序：
         *  运行流程：
         *
         * 1、线程池创建,准备好core数量的核心线程,准备接受任务
         * 2、新的任务进来,用core准备好的空闲线程执行。
         * (1) 、core满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行
         * (2)、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
         * (3) 、max都执行好了。Max-core数量空闲的线程会在keepAlivelime指定的时间后自动销毁。最终保持到core大小
         * (4) 、如果线程数开到了max的数量,还有新任务进来,就会使用reject指定的拒绝策略进行处理
         * 3、所有的线程创建都是由指定的factory创建的。
         *
         *  new LinkedBlockingQueue<>() 默认是Integer的最大值，可能会导致内存不够
         *
         *  一个线程池，core 7，max 20，queue 50，100并发进来怎么分配？
         *  7个会立即执行，50个会进入队列，再开13个进行执行。剩下30个就使用拒绝策略
         *  如果不行抛弃还要执行，就设置拒绝策略为 CallerRunsPolicy
         *
         *  开发中为什么使用线程池？
         *      降低资源的消耗
         *          通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗提高响应速度
         *      提高响应速度
         *          因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
         *      提高线程的可管理性
         *          线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。
         *          无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
         */
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(5,
                        200,
                        10,
                        TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(100000),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());

//        Executors.newCachedThreadPool(); core是0，所有任务都可以回收
//        Executors.newFixedThreadPool(10);固定大小，core=max，都不可以回收、
//        Executors.newScheduledThreadPool(10);定时任务的线程池
//        Executors.newSingleThreadExecutor();单线程的线程池，后台从队列里获取任务，挨个执行

    }

    public static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }
    }

    public static class Runnable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }
    }

    public static class Callable01 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
            return i;
        }
    }

    /**
     * CompletableFuture 使用
     * CompletableFuture 异步编排 实现了Future接口
     * 业务场景:查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
     * //1.获取sku的基本信息 0.5s
     * //2.获取sku的图片信息 0.5s
     * //3.获取sku的促销信息 1s
     * //4.获取spu的所有销售属性1s
     * //5.获取规格参数组及组下的规格参数 1.5s
     * //6. spu详情
     * 假如商品详情页的每个查询,需要如下标注的时间才能完成那么,用户需要6.5s后才能看到商品详情页的内容。很显然是不能接受的。
     * 如果有多个线程同时完成这6步操作,也许只需要1.5s即可完成响应。
     * <p>
     * 2、计算完成州回调方法
     * whenComplete可以处理正常和异常的计算结果, exceptionally处理异常情况.
     * whenComplete和whenCompleteAsync的区别:
     * whenComplete:是执行当前任务的线程执行继续执行whenComplete的任务。te:whenCompleteAsync:是执行把whenCompleteAsync这个任务继续提交给线程池来进行执行。
     * 方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
     * <p>
     *
     * 3, handle方法
     * 和complete一样,可对结果做最后的处理(可处理异常) ,可改变返回值。
     *
     * 4、线程串行化方法
     *  thenApply方法:当一个线程依赖另一个线程时，获取上一个任务返回的结果,并返回当前任务的返回值。
     *  thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
     *  thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行thenRun的后续操作
     *  带有Async默认是异步执行的。同之前。以上都要前置任务成功完成。
     *
     *  5、两任务组合-都要完成
     *      两个任务必须都完成,触发该任务。
     *      thenCombine:组合两个future,获取两个future的返回结果,并返回当前任务的返回值
     *      thenAcceptBoth:组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。
     *      runAtterBoth:组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务。
     *
     * 6、两任务组合-一个完成
     *      当两个任务中,任意一个future任务完成的时候,执行任务。
     *      applytoEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
     *      acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
     *      runAfterEither:两个任务有一个执行完成,不需要获取future的结果,处理任务,也没有返回值。
     *
     * 7、多任务组合
     *      allof:等待所有任务完成
     *      anyof:只要有一个任务完成
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("completableFuture...start...");

        // 1、使用
//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果：" + i);
//        }, executorService);
//
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executorService);


        // 2、计算完成州回调方法-- 方法完成后的感知
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 0;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executorService).whenComplete((result,exception)->{
//            // whenComplete 虽然能感知到异常信息，但是没法修改返回数据
//            System.out.println("异步任务执行完了，结果是："+result+";异常是："+exception);
//        }).exceptionally(throwable -> {
//            // exceptionally 可以感知异常，同时返回一个默认值
//            return 10;
//        });

        // 3、handle方法-- 方法完成后的处理
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 0;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executorService).whenComplete((result, exception) -> {
//            // whenComplete 虽然能感知到异常信息，但是没法修改返回数据
//            System.out.println("异步任务执行完了，结果是：" + result + ";异常是：" + exception);
//        }).handle((result, throwable) -> {
//            if (throwable != null) {
//                return 0;
//            }
//            return result;
//        });


        //4、线程串行化
        /**
         *  1)、thenRunxxx：不能获取到上一步的执行结果
         *      .thenRunAsync(() -> {
         *             System.out.println("线程2启动了。。。");
         *         }, executorService);
         *  2）、thenAcceptxxx：能接收上一步结果，无返回值，不能感知和处理异常
         *
         *  3）、thenApplyxxx：能接收上一步的结果，还有返回值
         */
//        CompletableFuture<Void> thenRunAsync = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 0;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executorService).thenRunAsync(() -> {
//            System.out.println("线程2启动了。。。");
//        }, executorService);
//
//        CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 4;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executorService).thenAcceptAsync(result -> {
//            System.out.println("线程2启动了。。。" + result);
//        }, executorService);
//        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 4;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executorService).thenApplyAsync(result -> {
//            System.out.println("线程2启动了。。。" + result);
//            return "Hello " + result;
//        }, executorService);

        // 5、两任务组合-都要完成
        /**
         * 两个任务必须都完成,触发该任务。
         * thenCombine:组合两个future,获取两个future的返回结果,并返回当前任务的返回值
         * thenAcceptBoth:组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。
         * runAtterBoth:组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务。
         */
//        CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
//            System.out.println("任务1线程：" + Thread.currentThread().getId());
//            int i = 10 / 4;
//            System.out.println("任务1线程结束");
//            return i;
//        }, executorService);
//
//        CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
//            System.out.println("任务2线程：" + Thread.currentThread().getId());
//            try {
//                Thread.sleep(2000);
//                System.out.println("任务2线程结束");
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            return "Hello";
//        }, executorService);

//        future1.runAfterBothAsync(future2,()->{
//            System.out.println("任务3开始。。。");
//        });

//        future1.thenAcceptBothAsync(future2,(f1,f2)->{
//            System.out.println("任务3开始:"+f1+"===>"+f2);
//        },executorService);

//        CompletableFuture<String> future = future1.thenCombineAsync(future2, (f1, f2) -> {
//            System.out.println("任务3开始:"+f1+"===>"+f2);
//            return f1 + ":" + f2 + "===>" + "Haha";
//        }, executorService);

        // 6、当两个任务中,任意一个future任务完成的时候,执行任务3。
        /**
         * runAfterEitherAsync:不感知结果，自己没有返回值
         * acceptEitherAsync:感知结果，自己没有返回值
         * applyToEitherAsync:感知结果，自己有返回值
         */
//        future1.runAfterEitherAsync(future2, ()->{
//            System.out.println("任务3开始。。。");
//        },executorService);

//        future1.acceptEitherAsync(future2, (result)->{
//            System.out.println("任务3开始。。。,结果："+result);
//        },executorService);

//        CompletableFuture<String> future = future1.applyToEitherAsync(future2, (result) -> {
//            System.out.println("任务3开始。。。,结果：" + result);
//            return "Hahaha==>" + result;
//        }, executorService);

        // 7、多任务组合
        // allof:等待所有任务完成
        // anyof:只要有一个任务完成
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品图片地址");
            return "hello.jpg";
        }, executorService);
        CompletableFuture<String> futureAttrs = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品属性");
            return "黑色+256G";
        }, executorService);
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品介绍");
            return "Huawei";
        }, executorService);

//        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttrs, futureDesc);
//        allOf.get();//阻塞线程，获取结果
//
//        System.out.println("completableFuture...end..." + futureImg.get() + "==>" + futureAttrs.get() + "==>" + futureDesc.get());

        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttrs, futureDesc);
        anyOf.get();
        System.out.println("completableFuture...end..." + anyOf.get());

    }

}
